﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Colorific {
	public enum TokenType {
		IGNORED,
		METHOD,
		VARIABLE,
		PROPERTY,
	}

	public class Token {
		public readonly int Pos;
		public readonly int Length;
		public readonly TokenType Type;
		public Token(int pos, int length, TokenType type) {
			Pos = pos;
			Length = length;
			Type = type;
		}
	}

	public class CSharpLexer {
		private string s;
		private int pos = 0;
		private char prefix = ' ';

		private Token MakeToken(string lexeme) {
			// Decide on type
			int start = pos - lexeme.Length;
			Trim();
			TokenType type = TokenType.IGNORED;
			if (FirstChar() == '(') type = TokenType.METHOD;
			else if (Char.IsLower(lexeme[0])) type = TokenType.VARIABLE;
			else if (prefix == '.') type = TokenType.PROPERTY;

			return new Token(start, lexeme.Length, type);
		}

		private void Trim() {
			while (s.StartsWith(" ") || s.StartsWith("\t")) {
				Next(true);
			}
		}

		private void Next(bool keepPrefix) {
			if (keepPrefix == false) prefix = s[0];
			Advance(s[0] == '\\' ? 2 : 1);
		}

		private void Advance(int n) {
			for (int i = 0 ; i < n ; i++) {
				s = s.Substring(1);
				pos++;
			}
		}

		private bool Accept(string expected) {
			if (s.StartsWith(expected)) {
				Advance(expected.Length);
				return true;
			}
			return false;
		}

		private void SkipTo(string end) {
			while (s.Length > 0) {
				if (Accept(end)) return;
				Next(false);
			}
		}

		private char FirstChar() {
			return s[0];
		}

		public IEnumerable<Token> Scan(string source) {
			s = source;
			Trim();
			while (s.Length > 0) {
				Trim();
				if (s.Length == 0) break;
				// check alphanumeric first
				if (Accept("/*")) {
					SkipTo("*/");
					continue;
				}
				if (Accept("\"")) {
					SkipTo("\"");
					continue;
				}
				if (Accept("//")) {
					SkipTo("\n");
					continue;
				}
				if (Char.IsLetter(FirstChar()) || FirstChar() == '_') {
					string nextWord = ReadWord();
					yield return MakeToken(nextWord);
					continue;
				}
				// We can't handle it
				Next(false);
			}
		}

		public string ReadWord() {
			StringBuilder word = new StringBuilder();
			while (s.Length > 0) {
				char c = FirstChar();
				if (c == '_' || Char.IsLetterOrDigit(c)) {
					word.Append(c);
					Next(true);
				} else {
					break;
				}
			}
			return word.ToString();
		}

	}
}

